﻿using System;
using BGShell.PowerShell.Controls;
using BGShell.PowerShell.Runspaces;

namespace BGShell.PowerShell.LanguageService
{
    internal sealed partial class TabExpansionHandler
    {
        private readonly IRunspaceProxy _runspace;
        private readonly PSLanguageService _langSvc;

        private Action _callback;
        private BGShellPipelineProxy _pipeline;

        private LastWord _lastWord;
        private String _initialText;

        private String[] _matches;
        private String _previousText;
        private Int32 _currentMatch;

        public TabExpansionHandler(IRunspaceProxy runspace, PSLanguageService langSvc)
        {
            _runspace = runspace;
            _langSvc = langSvc;

            Reset();
        }

        public void Reset()
        {
            CancelAsyncRequests();

            _currentMatch = int.MinValue;
            _previousText = null;
            _lastWord = null;
        }

        public void Next()
        {
            if (GetTextToExpand() == _previousText)
            {
                SelectNext();
            }
            else
            {
                BeginTabExpansion(SelectNext);
            }
        }

        public void Prev()
        {
            if (GetTextToExpand() == _previousText)
            {
                SelectPrev();
            }
            else
            {
                BeginTabExpansion(SelectPrev);
            }
        }

        private string GetTextToExpand()
        {
            return _langSvc.Reader.Text.Substring(0, _langSvc.Reader.SelectionStart);
        }

        private string CurrentMatch
        {
            get 
            {
                if ((_matches != null) && (_matches.Length > 0))
                {
                    return Prefix + _matches[_currentMatch] + _lastWord.ClosingQuote;
                }

                return _initialText;
            }
        }

        private string Prefix
        {
            get { return _initialText.Substring(0, _lastWord.ReplacementIndex); }
        }

        private void SelectNext()
        {
            int m = _currentMatch;

            if (m == int.MinValue)
            {
                m = -1;
            }

            if (_matches.Length > 0)
            {
                m = (m + 1) % _matches.Length;
            }

            SetMatch(m);
        }

        private void SelectPrev()
        {
            int m = _currentMatch;

            if (m == int.MinValue)
            {
                m = _matches.Length;
            }

            if (m == 0)
            {
                m = _matches.Length;
            }

            if (_matches.Length > 0)
            {
                m--;
            }

            SetMatch(m);
        }

        private void SetMatch(int index)
        {
            if (_currentMatch != index)
            {
                _currentMatch = index;

                var str = CurrentMatch;

                if (!string.IsNullOrEmpty(str))
                {
                    _previousText = str;
                    _langSvc.Reader.Text = str;
                }
            }
        }

        private void BeginTabExpansion(Action callback)
        {
            Reset();

            _initialText = GetTextToExpand();
            _lastWord = new LastWord(_initialText);

            _callback = callback;
            _pipeline = _runspace.BeginGetTabExpansions(GotExpansions, _initialText, _lastWord.Value);
        }

        internal void GotExpansions(String[] matches)
        {
            _langSvc.Control.BeginInvokeIfRequired(GotExpansionsUnsafe, matches);
        }

        internal void GotExpansionsUnsafe(String[] matches)
        {
            var prefixEmpty = Prefix.Length == 0;

            Array.Sort(matches, StringComparer.CurrentCultureIgnoreCase);

            for (int i = 0; i < matches.Length; i++)
            {
                if (matches[i].LastIndexOf(' ') > -1)
                {
                    string str = string.Empty;

                    if (prefixEmpty)
                    {
                        str = "& ";
                    }

                    if (String.IsNullOrEmpty(_lastWord.ClosingQuote))
                    {
                        str += '\'';
                    }

                    str += matches[i];

                    if (String.IsNullOrEmpty(_lastWord.ClosingQuote))
                    {
                        str += '\'';
                    }

                    matches[i] = str;
                }
            }

            _pipeline = null;
            _matches = matches;

            if (_callback != null)
            {
                _callback();
                _callback = null;
            }
        }

        internal void CancelAsyncRequests()
        {
            _callback = null;

            if (_pipeline != null)
            {
                _pipeline.StopAsync();
                _pipeline = null;
            }
        }
    }
}
